package com.joinway.admin.service;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.joinway.admin.bean.view.Sql2BeanView;
import com.joinway.admin.bean.view.Sql2CodeView;
import com.joinway.admin.bean.view.Sql2PageView;
import com.joinway.admin.platform.PlatformAdmin;
import com.joinway.framework.admin.core.service.file.FileManager;
import com.joinway.framework.admin.core.service.file.FileManager.Server;
import com.joinway.framework.admin.core.service.file.FileRepository;
import com.joinway.framework.admin.core.utils.VelocityProcessor;
import com.joinway.framework.extension.groovy.DomainUtils;
import com.joinway.framework.extension.utils.DataUtils;
import com.joinway.framework.text.sql.DdlParser;
import com.joinway.platform.bean.AdminPageConfig;
import com.joinway.platform.bean.AdminPageItem;
import com.joinway.platform.utils.Ddl2CodeBuilder;
import com.joinway.platform.utils.DdlEntity;

@Service
public class AutoCodeService {

	private final static Logger log = LoggerFactory.getLogger(AutoCodeService.class);
	
	private static final String CODE_DIR = "code";
	private static final String CODE_TEMPLATE = "template/preview.html";
	private static final String CODE_KEY = "html";
	
	private static final String GROOVY_SUFFIX = ".groovy";
	private static final String JAVA_SUFFIX = ".java";
	private static final String HTML_SUFFIX = ".html";
	private static final String XML_SUFFIX = ".xml";
	private static final String JSP_SUFFIX = ".jsp";
	
	@Autowired VelocityProcessor processor;
	
	
	public Sql2BeanView convertDdlToBean(String domainModuleName, String sql, Map<String, String> masks, Map<String, String> formats, Map<String, String> dates) throws Exception {
		Sql2BeanView view = new Sql2BeanView();
		
		Ddl2CodeBuilder builder = new Ddl2CodeBuilder(sql);
		
		String path = CODE_DIR + "/" + FileManager.getAvailableName();
		Server serverType = PlatformAdmin.COMPACT_MODE.equals(System.getProperty(PlatformAdmin.MODE_KEY)) ? Server.App : Server.Web;
		String fileDir = FileManager.getFileRoot(serverType) + path + "/";

		String className = DdlEntity.parseToDomainName(sql);
		String javaFileName = className  + GROOVY_SUFFIX;

		/*
		 * build domain bean
		 */
		buildCode(javaFileName, path
			, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					builder.buildDomainBean(domainModuleName, file, masks, formats, dates);
				}
			}, new CodeDownloadBuilder(){
				@Override
				public void build(String url, String fileName) {
					view.setDomainBeanDownloadUrl(url);
					view.setDomainBeanName(fileName);
				}
			}
		);
		
		/*
		 * build domain bean preview
		 */
		buildPreview(javaFileName, path, fileDir, new CodePreviewBuilder(){
			@Override
			public void build(String url) {
				view.setDomainBeanPreviewUrl(url);
			}
		});
		
		return view;
	}
	
	public Sql2CodeView convertDdlToCode(String domainModuleName, String mapperModuleName, String ddl, List<String> columns, Map<String, String> conditions) throws Exception {
		Sql2CodeView view = new Sql2CodeView();
		
		Ddl2CodeBuilder builder = new Ddl2CodeBuilder(ddl);
		
		String path = CODE_DIR + "/" + FileManager.getAvailableName();
		Server serverType = PlatformAdmin.COMPACT_MODE.equals(System.getProperty(PlatformAdmin.MODE_KEY)) ? Server.App : Server.Web;
		String fileDir = FileManager.getFileRoot(serverType) + path + "/";

		String className = DdlEntity.parseToDomainName(ddl);

		/*
		 * build mybatis xml config file
		 */
		String xmlFileName = className + "Mapper" + XML_SUFFIX;
		buildCode(xmlFileName, path
			, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					builder.buildMyBatisXml(mapperModuleName, domainModuleName, columns, conditions, file);
				}
			}, new CodeDownloadBuilder(){
				@Override
				public void build(String url, String fileName) {
					view.setMybatisXmlDownloadUrl(url);
					view.setMybatisXmlName(fileName);
				}
			}
		);
		
		/*
		 * build mybatis xml config file preview
		 */
		buildPreview(xmlFileName, path, fileDir, new CodePreviewBuilder(){
			@Override
			public void build(String url) {
				view.setMybatisXmlPreviewUrl(url);
			}
		});
		
		/*
		 * build dao
		 */
		String daoFileName = className + "Mapper" + JAVA_SUFFIX;
		buildCode(daoFileName, path
			, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					builder.buildDao(mapperModuleName, domainModuleName, columns, conditions, file);
				}
			}, new CodeDownloadBuilder(){
				@Override
				public void build(String url, String fileName) {
					view.setDaoDownloadUrl(url);
					view.setDaoName(fileName);
				}
			}
		);
		
		/*
		 * build dao preview
		 */
		buildPreview(daoFileName, path, fileDir, new CodePreviewBuilder(){
			@Override
			public void build(String url) {
				view.setDaoPreviewUrl(url);
			}
		});
		
		/*
		 * build dao repository
		 */
		String daoRepositoryFileName = className + "Repository" + JAVA_SUFFIX;
		buildCode(daoRepositoryFileName, path
			, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					builder.buildDaoRepository(mapperModuleName, domainModuleName, columns, conditions, file);
				}
			}, new CodeDownloadBuilder(){
				@Override
				public void build(String url, String fileName) {
					view.setDaoRepositoryDownloadUrl(url);
					view.setDaoRepositoryName(fileName);
				}
			}
		);
		
		/*
		 * build dao repository preview
		 */
		buildPreview(daoRepositoryFileName, path, fileDir, new CodePreviewBuilder(){
			@Override
			public void build(String url) {
				view.setDaoRepositoryPreviewUrl(url);
			}
		});
		
		return view;
	}
	
	public Sql2PageView convertDdlToPage(String ddl, List<AdminPageItem> items, AdminPageConfig config) throws Exception {
		Sql2PageView view = new Sql2PageView();
		
		Ddl2CodeBuilder builder = new Ddl2CodeBuilder(ddl);
		
		String path = CODE_DIR + "/" + FileManager.getAvailableName();
		
		Server serverType = PlatformAdmin.COMPACT_MODE.equals(System.getProperty(PlatformAdmin.MODE_KEY)) ? Server.App : Server.Web;
		String fileDir = FileManager.getFileRoot(serverType) + path + "/";

		String table = StringUtils.capitalize(DomainUtils.convertToDomainFieldName(DdlParser.parse(ddl).getName()));
		String pageName = table + JSP_SUFFIX;
		String codeName = table + "TableService" + JAVA_SUFFIX;
		
		/*
		 * build admin page
		 */
		buildCode(pageName, path
			, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					view.setLocalFile(file);
					builder.buildAdminPage(items, config, file);
				}
			}, new CodeDownloadBuilder(){
				@Override
				public void build(String url, String fileName) {
					view.setDownloadUrl(url);
					view.setPageName(fileName);
				}
			}
		);
		
		/*
		 * build admin page preview
		 */
		buildPreview(pageName, path, fileDir, new CodePreviewBuilder(){
			@Override
			public void build(String url) {
				view.setPreviewUrl(url);
			}
		});
		
		if(config.isOverrided()){
			/*
			 * build table service
			 */
			buildCode(codeName, path
				, new FileRepository<String>(){
					@Override
					public void save(String file, String context) throws Exception {
						builder.buildTableService(config, file);
					}
				}, new CodeDownloadBuilder(){
					@Override
					public void build(String url, String fileName) {
						view.setServiceDownloadUrl(url);
						view.setServiceName(codeName);
					}
				}
			);
			
			/*
			 * build table service preview
			 */
			buildPreview(codeName, path, fileDir, new CodePreviewBuilder(){
				@Override
				public void build(String url) {
					view.setServicePreviewUrl(url);
				}
			});
		}
		
		return view;
	}
	
//	@Deprecated
//	public Sql2BeanView convertSqlToBean(String domainModuleName, String sql) throws Exception {
//		Sql2BeanView view = new Sql2BeanView();
//		
//		Ddl2CodeBuilder builder = new Ddl2CodeBuilder(sql);
//		
//		String path = CODE_DIR + "/" + FileManager.getAvailableName();
//		String fileDir = FileManager.getFileRoot(Server.Web) + path + "/";
//
//		String className = DdlEntity.parseToDomainName(sql);
//		String javaFileName = className  + JAVA_SUFFIX;
//
//		/*
//		 * build domain bean
//		 */
//		buildCode(javaFileName, path
//			, new FileRepository(){
//				@Override
//				public void save(String file) throws Exception {
//					builder.buildDomainBean(domainModuleName, file);
//				}
//			}, new CodeDownloadBuilder(){
//				@Override
//				public void build(String url, String fileName) {
//					view.setDomainBeanDownloadUrl(url);
//					view.setDomainBeanName(fileName);
//				}
//			}
//		);
//		
//		/*
//		 * build domain bean preview
//		 */
//		buildPreview(javaFileName, path, fileDir, new CodePreviewBuilder(){
//			@Override
//			public void build(String url) {
//				view.setDomainBeanPreviewUrl(url);
//			}
//		});
//		
//		return view;
//	}
	
//	@Deprecated
//	public Sql2CodeView convertSqlToCode(String domainModuleName, String mapperModuleName, String sql) throws Exception {
//		Sql2CodeView view = new Sql2CodeView();
//		
//		Ddl2CodeBuilder builder = new Ddl2CodeBuilder(sql);
//		
//		String path = CODE_DIR + "/" + FileManager.getAvailableName();
//		String fileDir = FileManager.getFileRoot(Server.Web) + path + "/";
//
//		String className = DdlEntity.parseToDomainName(sql);
//
//		/*
//		 * build mybatis xml config file
//		 */
//		String xmlFileName = className + "Mapper" + XML_SUFFIX;
//		buildCode(xmlFileName, path
//			, new FileRepository(){
//				@Override
//				public void save(String file) throws Exception {
//					builder.buildMyBatisXml(mapperModuleName, domainModuleName, file);
//				}
//			}, new CodeDownloadBuilder(){
//				@Override
//				public void build(String url, String fileName) {
//					view.setMybatisXmlDownloadUrl(url);
//					view.setMybatisXmlName(fileName);
//				}
//			}
//		);
//		
//		/*
//		 * build mybatis xml config file preview
//		 */
//		buildPreview(xmlFileName, path, fileDir, new CodePreviewBuilder(){
//			@Override
//			public void build(String url) {
//				view.setMybatisXmlPreviewUrl(url);
//			}
//		});
//		
//		/*
//		 * build dao
//		 */
//		String daoFileName = className + "Mapper" + JAVA_SUFFIX;
//		buildCode(daoFileName, path
//			, new FileRepository(){
//				@Override
//				public void save(String file) throws Exception {
//					builder.buildDao(mapperModuleName, domainModuleName, file);
//				}
//			}, new CodeDownloadBuilder(){
//				@Override
//				public void build(String url, String fileName) {
//					view.setDaoDownloadUrl(url);
//					view.setDaoName(fileName);
//				}
//			}
//		);
//		
//		/*
//		 * build dao preview
//		 */
//		buildPreview(daoFileName, path, fileDir, new CodePreviewBuilder(){
//			@Override
//			public void build(String url) {
//				view.setDaoPreviewUrl(url);
//			}
//		});
//		
//		/*
//		 * build dao repository
//		 */
//		String daoRepositoryFileName = className + "Repository" + JAVA_SUFFIX;
//		buildCode(daoRepositoryFileName, path
//			, new FileRepository(){
//				@Override
//				public void save(String file) throws Exception {
//					builder.buildDaoRepository(mapperModuleName, domainModuleName, file);
//				}
//			}, new CodeDownloadBuilder(){
//				@Override
//				public void build(String url, String fileName) {
//					view.setDaoRepositoryDownloadUrl(url);
//					view.setDaoRepositoryName(fileName);
//				}
//			}
//		);
//		
//		/*
//		 * build dao repository preview
//		 */
//		buildPreview(daoRepositoryFileName, path, fileDir, new CodePreviewBuilder(){
//			@Override
//			public void build(String url) {
//				view.setDaoRepositoryPreviewUrl(url);
//			}
//		});
//		
//		return view;
//	}
	
	void buildPreview(String fileName, String path, String fileDir, CodePreviewBuilder builder){
		try{
			String htmlName = FileManager.getAvailableName() + HTML_SUFFIX;
			Server serverType = PlatformAdmin.COMPACT_MODE.equals(System.getProperty(PlatformAdmin.MODE_KEY)) ? Server.App : Server.Web;
			String url = FileManager.save(path, htmlName, serverType, new FileRepository<String>(){
				@Override
				public void save(String file, String context) throws Exception {
					String text = DataUtils.filterHtml(FileUtils.readFileToString(new File(fileDir, fileName)));
					
					Map<String, Object> model = new HashMap<>();
					model.put(CODE_KEY, text);
					processor.process(CODE_TEMPLATE, model, file);
				}
			});
			
			builder.build(url);
		}catch(Exception e){
			log.error("Failed to build " + fileName + " preview", e);
		}
	}
	
	void buildCode(String fileName, String path, FileRepository repo, CodeDownloadBuilder builder){
		try{
			Server serverType = PlatformAdmin.COMPACT_MODE.equals(System.getProperty(PlatformAdmin.MODE_KEY)) ? Server.App : Server.Web;
			String url = FileManager.save(path, fileName, serverType, repo);
			builder.build(url, fileName);
		}catch(Exception e){
			log.error("Failed to build " + fileName, e);
		}
	}
	
	interface CodePreviewBuilder{
		void build(String url);
	}
	
	interface CodeDownloadBuilder{
		void build(String url, String fileName);
	}
}
